package com.example.online.music.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.online.music.common.R;
import com.example.online.music.common.RandomCodeGenerate;
import com.example.online.music.config.AlipayCommonConfig;
import com.example.online.music.constant.Constants;
import com.example.online.music.mapper.CartListMapper;
import com.example.online.music.mapper.PayItemMapper;
import com.example.online.music.mapper.PayMapper;
import com.example.online.music.mapper.SongMapper;
import com.example.online.music.model.domain.Pay;
import com.example.online.music.model.domain.PayItem;
import com.example.online.music.model.domain.Song;
import com.example.online.music.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 支付宝支付接口
 * @Author: Jason
 * @date: 2023.6.29
 */
@Service
public class PayServieImpl implements PayService {

    @Autowired
    private SongMapper songMapper;

    @Autowired
    private PayMapper payMapper;

    @Autowired
    private PayItemMapper payItemMapper;

    @Autowired
    private AlipayCommonConfig alipayCommonConfig;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private CartListMapper cartListMapper;

    /**
     * 支付宝沙箱通用支付功能
     * @param songIds 歌曲ID集合
     * @param userId 用户ID
     * @param payType 支付类型 1表示直接支付 2表示购物车结算
     * @return
     * @throws Exception
     */
    @Override
    public R commonAlipay(List<Integer> songIds, Integer userId, Integer payType) {

        if(songIds == null || songIds.size() < 1){
            R.error("接口传入参数错误");
        }

        String alipayResult = null;

        try {
            List<Song> songs = songMapper.selectBatchIds(songIds);
            if(songs == null || songs.size() < 1){
                return R.error("查询歌曲数据错误");
            }

            //组装支付清单和清单子项并求清单的总金额
            //产生清单编号
            String payNo = RandomCodeGenerate.createOrderNo();
            List<PayItem> payItems = new ArrayList<>();
            BigDecimal totalAmount = BigDecimal.valueOf(0.0);
            for (Song s: songs) {
                PayItem item = new PayItem();
                item.setPrice(s.getPrice());
                item.setSongId(s.getId());
                item.setTradeNo(payNo);
                item.setUserId(userId);
                payItems.add(item);
                BigDecimal songPrice = BigDecimal.valueOf(item.getPrice());
                totalAmount = totalAmount.add(songPrice);
            }

            //金额<=0则支付宝订单异常
            if(totalAmount.compareTo(BigDecimal.ZERO) <= 0){
                return R.warning("订单金额有错误");
            }

            //组装订单
            Pay pay = new Pay();
            pay.setPayStatus(Constants.PAY_STATUS_1);
            pay.setPayType(payType);
            pay.setSubject("歌曲下载");
            pay.setAmount(totalAmount.doubleValue());
            pay.setTradeNo(payNo);
            pay.setUserId(userId);
            pay.setPayItems(payItems);

            //付款清单和清单子项暂存，待支付成功后核销
            redisTemplate.opsForValue().set(Constants.ALIPAY_REDIS_KEY + pay.getTradeNo().trim(),
                    JSONObject.toJSONString(pay));

            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(alipayCommonConfig.getGateway_url(),
                    alipayCommonConfig.getApp_id(),
                    alipayCommonConfig.getApp_private_key(),
                    alipayCommonConfig.getFormat(),alipayCommonConfig.getCharset(),
                    alipayCommonConfig.getAlipay_public_key(),
                    alipayCommonConfig.getSign_type());

            //设置请求参数
            AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
            //同步回调
            alipayRequest.setReturnUrl(alipayCommonConfig.getReturn_url());
            //支付宝支付成功服务器异步回调核销
            alipayRequest.setNotifyUrl(alipayCommonConfig.getNotify_url());

            Map<String,Object> bizContent = new HashMap<>();
            //订单号
            bizContent.put("out_trade_no",pay.getTradeNo());
            //销售产品码，与支付宝签约的产品码名称。目前仅支付FAST_INSTANT_TRADE_PAY
            bizContent.put("product_code","FAST_INSTANT_TRADE_PAY");
            //订单金额
            bizContent.put("total_amount",pay.getAmount());
            //中文乱码处理
            bizContent.put("subject",new String(pay.getSubject().getBytes("UTF-8"), "UTF-8"));
            alipayRequest.setBizContent(JSONObject.toJSONString(bizContent));
            alipayResult = alipayClient.pageExecute(alipayRequest).getBody();
        }catch (AlipayApiException e){
            e.printStackTrace();
            return R.error("请求支付宝异常 | "+e.getMessage());
        }catch (Exception e){
            e.printStackTrace();
            return R.error("接口异常 | "+e.getMessage());
        }
        return R.success("",alipayResult);
    }

    /**
     * 支付宝沙箱支付服务器端异步回调功能，应用上线后可以和同步并用
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    @Transactional( rollbackFor=Exception.class)
    public R commonPayNotify(HttpServletRequest request) throws Exception{

        System.out.println("支付宝通用支付回调");

        if(request.getParameter("trade_status").equals("TRADE_SUCCESS")){

            Map<String, String> params = new HashMap<>();
            Map<String, String[]> parameterMap = request.getParameterMap();
            for (String name: parameterMap.keySet() ) {
                params.put(name, request.getParameter(name));
            }

            //订单交易号=商户订单号
            String outTradeNo = params.get("out_trade_no");
            //买家付款时间
            String gmtPayment = params.get("gmt_payment");
            //支付宝的交易凭证
            String alipayNo = params.get("trade_no");
            //签名
            String sign = params.get("sign");
            String contentV1 = AlipaySignature.getSignCheckContentV1(params);
            boolean checkSignature = AlipaySignature.rsa256CheckContent(contentV1,
                    sign, alipayCommonConfig.getAlipay_public_key(), alipayCommonConfig.getCharset());

            //支付宝验签
            R r = null;
            if(checkSignature){
                //订单子项核销，更新订单状态并保存订单子项
                String payStr = redisTemplate.opsForValue().get(Constants.ALIPAY_REDIS_KEY + outTradeNo.trim());
                r = afterPayBusiness(payStr, alipayNo);
                //核销后清空缓存
                redisTemplate.delete(Constants.ALIPAY_REDIS_KEY + outTradeNo.trim());
            }else {
                return R.error("支付验签失败，需手动核销清单项");
            }
          return r;
        }
        return R.error("支付回调失败，需手动核销清单项");
    }

    /**
     * 当应用上线后，方法只负责跳转，不做复杂的核销业务，
     * 核销业务应在异步回调中执行
     * @param tradeNo
     * @param sign
     * @param response
     * @throws Exception
     */
    @Override
    @Transactional( rollbackFor=Exception.class)
    public void commonPayReturn(String tradeNo, String sign, HttpServletResponse response) throws Exception {
        //订单子项核销，更新订单状态并保存订单子项
        String payStr = redisTemplate.opsForValue().get(Constants.ALIPAY_REDIS_KEY + tradeNo.trim());
        afterPayBusiness(payStr, tradeNo);
        //核销后清空缓存
        redisTemplate.delete(Constants.ALIPAY_REDIS_KEY + tradeNo.trim());
        response.setHeader("refresh", "1;url="+alipayCommonConfig.getRefresh_url());
    }

    private R afterPayBusiness(String payStr, String alipayNo){

        if(payStr == null){
            return R.error("数据从缓存中取出错误");
        }

        Pay pay = JSONObject.parseObject(payStr, Pay.class);

        if(pay == null){
            return R.error("缓存数据转换类型错误");
        }

        //支付清单保存
        pay.setAlipayNo(alipayNo);
        payMapper.insert(pay);
        //支付清单子项保存，后期可以优化批量插入
        List<PayItem> payItems = pay.getPayItems();
        payItems.forEach(payItem -> payItemMapper.insert(payItem));

        //如果是购物车结算，即payType=2
        if(pay.getPayType() == 2){
            List<Integer> clistIds = new ArrayList<>();
            payItems.forEach(p -> clistIds.add(p.getSongId()));
            cartListMapper.delCartListBySongId(pay.getUserId(), clistIds);
        }
        return R.success("支付核销成功");
    }

    /**
     * 返回某用户的所有支付的歌曲清单
     * @param userId
     * @return
     */
    @Override
    public R allPayList(Integer userId) {
        QueryWrapper<PayItem> qw = new QueryWrapper<>();
        qw.eq("user_id",userId);
        return R.success("",payItemMapper.selectList(qw));
    }

}
